#!/usr/bin/env php
<?php
error_reporting(E_ALL);
$root = dirname(__DIR__);

require $root . '/vendor/autoload.php';
require $root . '/vendor/yiisoft/yii2/Yii.php';
require $root . '/common/config/bootstrap.php';

global $globalTemplate;
global $globalBeCompleted;
$globalTemplate = [
    'css' => $root . '/builder/template/create_tpl/css.php',
    'js' => $root . '/builder/template/create_tpl/js.php',
    'view' => $root . '/builder/template/create_tpl/view.php',
    'bundle' => $root . '/builder/template/create_tpl/bundle.php',
];
$globalBeCompleted = [];

$commandMap = [
    'css' => ['makeCss'],
    'js' => ['makeJs'],
    'view' => ['makeView'],
    'bundle' => ['makeBundle'],
    'all' => ['makeCss', 'makeJs', 'makeView', 'makeBundle'],
];

set_exception_handler(function ($exception) {
    if ($exception == null) {
        return;
    }

    // Avoid Recursive Error.
    restore_error_handler();
    restore_exception_handler();

    echo $exception->getMessage() . "\n";

    global $globalBeCompleted;
    if ($exception instanceof OverwriteCancelException && !empty($globalBeCompleted)) {
        foreach ($globalBeCompleted as $file) {
            $result = system('git restore ' . $file, $returnCode);
            if ($returnCode != 0) {
                echo ($result ?: 'Git Restore Failure. ') . "\n";
                exit(1);
            }

            echo "Git Restore File ******{$file}\n";
        }
    }

    exit(1);
});
set_error_handler(function ($errno, $errMsg, $errFile, $errLine) {
    throw new \Exception($errMsg, $errno);
});
register_shutdown_function(function () {
    $last = error_get_last();
    if ($last !== null && in_array($last['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING])) {
        echo ($last ? $last['message'] : 'Error') . "\n";
        exit(1);
    }
});

$args = getParams();
if (empty($args)) {
    die('Please input command. ' . "\n");
}

if (in_array('all', array_keys($args))) {
    unset(
        $args['css'],
        $args['js'],
        $args['view'],
        $args['bundle']
    );
}
foreach ($args as $command => $makePath) {
    if (!isset($commandMap[$command])) {
        continue;
    }

    foreach ($commandMap[$command] as $handler) {
        call_user_func($handler, $makePath);
    }
}

function makeCss($makePath, $destDir = __DIR__ . '/web/css')
{
    global $globalTemplate;
    global $globalBeCompleted;
    createDirIfNotFound($destDir);
    $filename = pathSplitByDot($makePath, function ($value) {
        return camel2Dot($value);
    });

    $destFile = $destDir . '/' . $filename . '.css';
    assertOverwrite($destFile);

    file_put_contents(
        $destFile,
        renderPhpFile($globalTemplate['css'], [
            'id' => $makePath,
        ])
    );

    echo 'The CSS file has been created successfully.' . PHP_EOL;

    $globalBeCompleted[] = $destFile;
}

function makeJs($makePath, $destDir = __DIR__ . '/web/js')
{
    global $globalTemplate;
    global $globalBeCompleted;

    createDirIfNotFound($destDir);
    $filename = pathSplitByDot($makePath, function ($value) {
        return camel2Dot($value);
    });

    $destFile = $destDir . '/' . $filename . '.js';
    assertOverwrite($destFile);

    file_put_contents(
        $destFile,
        renderPhpFile($globalTemplate['js'], [
            'id' => $filename,
        ])
    );

    echo 'The Javascript file has been created successfully.' . PHP_EOL;

    $globalBeCompleted[] = $destFile;
}

function makeView($makePath, $destDir = __DIR__ . '/views')
{
    global $globalTemplate;
    global $globalBeCompleted;

    $oldMakePath = $makePath;
    $makePath = trim(strtr($makePath, '\\', '/'), '/');
    if (!strpos($makePath, '/')) {
        $makePath .= '/index';
    }

    list($dirname, $filename) = explode('/', $makePath);
    $destDir = $destDir . '/' . $dirname;
    createDirIfNotFound($destDir);

    $destFile = $destDir . '/' . $filename . '.php';
    assertOverwrite($destFile);

    file_put_contents(
        $destFile,
        renderPhpFile($globalTemplate['view'], [
            'moduleName' => basename(__DIR__),
            'bundleId' => pathRemoveSeparator($oldMakePath, function ($value) { return ucfirst($value); }) . 'Asset',
            'id' => pathSplitByDot($oldMakePath, function ($value) {
                return camel2Dot($value);
            }),
        ])
    );

    echo 'The View file has been created successfully.' . PHP_EOL;

    $globalBeCompleted[] = $destFile;
}

function makeBundle($makePath, $destDir = __DIR__ . '/assets')
{
    global $globalTemplate;
    global $globalBeCompleted;

    $oldMakePath = $makePath;
    createDirIfNotFound($destDir);
    $filename = pathRemoveSeparator($makePath, function ($value) { return ucfirst($value); }) . 'Asset';

    $destFile = $destDir . '/' . $filename . '.php';
    assertOverwrite($destFile);

    file_put_contents(
        $destFile,
        renderPhpFile($globalTemplate['bundle'], [
            'moduleName' => basename(__DIR__),
            'bundleId' => $filename,
            'assetId' => pathSplitByDot($oldMakePath, function ($value) {
                return camel2Dot($value);
            }),
        ])
    );

    echo 'The Asset-Bundle file has been created successfully.' . PHP_EOL;

    $globalBeCompleted[] = $destFile;
}

function camel2Dot($string)
{
    return \yii\helpers\Inflector::camel2id($string, '.');
}

function createDirIfNotFound($path)
{
    if (!is_dir($path)) {
        mkdir($path, 0775, true);
    }
}

function pathRemoveSeparator($path, $perCallback = null)
{
    return str_replace('.', '', pathSplitByDot($path, $perCallback));
}

function pathSplitByDot($path, $perCallback = null)
{
    $dest = trim(strtr($path, ['\\' => '.', '/' => '.']), '.');
    if (is_callable($perCallback)) {
        $rest = [];
        foreach (explode('.', $dest) as $item) {
            $rest[] = call_user_func($perCallback, $item);
        }

        return implode('.', $rest);
    }

    return $dest;
}

class OverwriteCancelException extends \Exception
{
    public function getName()
    {
        return 'OverwriteCancelException';
    }
}

function assertOverwrite($file)
{
    if (is_file($file)) {
        echo 'Overwrite ' . basename($file) . '：[1 or 0] ';
        $answer = trim(fgets(STDIN));
        if ($answer == 0) {
            throw new OverwriteCancelException('User Press 0 And Quiting. ' . "\n", 1001);
        }
    }
}

function getParams()
{
    $rawParams = [];
    if (isset($_SERVER['argv'])) {
        $rawParams = $_SERVER['argv'];
        array_shift($rawParams);
    }

    $params = [];
    foreach ($rawParams as $param) {
        if (preg_match('/^--([\w-]*\w)(=(.*))?$/', $param, $matches)) {
            $name = $matches[1];
            $params[$name] = isset($matches[3]) ? $matches[3] : true;
        } else {
            $params[] = $param;
        }
    }
    return $params;
}

function renderPhpFile($_file_, $_params_ = [])
{
    $_obInitialLevel_ = ob_get_level();
    ob_start();
    ob_implicit_flush(false);
    extract($_params_, EXTR_OVERWRITE);
    try {
        require $_file_;
        return ob_get_clean();
    } catch (\Exception $e) {
        while (ob_get_level() > $_obInitialLevel_) {
            if (!@ob_end_clean()) {
                ob_clean();
            }
        }
        throw $e;
    } catch (\Throwable $e) {
        while (ob_get_level() > $_obInitialLevel_) {
            if (!@ob_end_clean()) {
                ob_clean();
            }
        }
        throw $e;
    }
}